﻿using AntdUI;
using DidaManager.Log;
using DidaManager.Native.SerialPort;
using LiteDb;
using NLog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static DidaManager.Native.SerialPort.Ymodem;
using LogManager = DidaManager.Log.LogManager;

namespace DidaManager.Native.Download
{
    enum DeviceCMDType
    {
        DBG,
        RESET,
        DWFIREWARE,
        INFO,
        STATE,
        FSFORMAT,
        DELCONFIGS
    }

    public class SerialDownload
    {
        private readonly SerialPortManager? _serialPortManager = null;
        private Dictionary<DeviceCMDType, string> m_dictDeviceTypeTips;
        private Ymodem? _ymodem = null;
        private string m_strDevDWFirewareRsp = "C";
        private string m_strDevResetRsp = "Within 5s";
        private bool m_bSerialNextFile = false;// 串口发送下一个的条件
        private bool _isDownloading = false; // 添加下载状态标志
        
        // 添加缺失的字段
        private int m_iSerialFileCount = 0;
        private int m_iSerialTotalFiles = 0;
        
        // 添加UI控件引用，用于Invoke调用
        private Control? _invokeControl = null;

        public object StopDownload()
        {
            try
            {
                // 检查是否有下载任务在进行
                if (!_isDownloading)
                {
                    return ApiResponse.Fail("当前没有下载任务在进行");
                }

                // 检查ymodem实例是否有效
                if (_ymodem == null)
                {
                    return ApiResponse.Fail("Ymodem实例无效");
                }

                // 停止下载，并清空下载状态和数据
                m_iSerialFileCount = 0;
                m_iSerialTotalFiles = 0;
                m_bSerialNextFile = false;
                
                try
                {
                    // 确保切换到命令模式并结束Ymodem传输
                    _ymodem.SetMode(SerialPortMode.Command);
                    _ymodem.YModemEnd();
                    
                    // 尝试发送中止命令
                    SendCMD(DeviceCMDType.RESET, 3, m_strDevResetRsp, "中止下载", false, false);
                }
                catch (Exception ex)
                {
                    LogManager.Error($"停止下载时发生错误: {ex.Message}");
                }
                finally
                {
                    _isDownloading = false;
                }

                return ApiResponse.Success("停止下载成功");
            }
            catch (Exception ex)
            {
                LogManager.Error($"停止下载失败: {ex.Message}");
                return ApiResponse.Fail($"停止下载失败: {ex.Message}");
            }
        }

        public SerialDownload(SerialPortManager serialPortManager, Control invokeControl = null!)
        {
            m_dictDeviceTypeTips = new Dictionary<DeviceCMDType, string>();
            _serialPortManager = serialPortManager;
            _invokeControl = invokeControl;

            _ymodem = _serialPortManager.GetYmodem();
            if (_ymodem == null)
            {
                LogManager.Error("ymodem is null");
                return;
            }
            _ymodem.NowDownloadProgressEvent += new EventHandler(NowDownloadProgressEvent);
            _ymodem.DownloadResultEvent += new EventHandler(SerialDownloadFinishEvent);
            // 提示信息
            m_dictDeviceTypeTips.Add(DeviceCMDType.DBG, "调试信息");
            m_dictDeviceTypeTips.Add(DeviceCMDType.DELCONFIGS, "删除配置中");
            m_dictDeviceTypeTips.Add(DeviceCMDType.DWFIREWARE, "更新固件中");
            m_dictDeviceTypeTips.Add(DeviceCMDType.FSFORMAT, "格式化文件系统中");
            m_dictDeviceTypeTips.Add(DeviceCMDType.INFO, "获取设备信息中");
            m_dictDeviceTypeTips.Add(DeviceCMDType.RESET, "设备重启中");
            m_dictDeviceTypeTips.Add(DeviceCMDType.STATE, "获取设备状态中");
        }

        #region 下载进度委托及事件响应
        private delegate void SerialNowDownloadProgress(string msg);
        private void NowDownloadProgressEvent(object? sender, EventArgs e)
        {
            // Use null-coalescing operator to handle potential null value
            string msg = Convert.ToString(sender) ?? string.Empty;
            SerialNowDownloadProgress count = new SerialNowDownloadProgress(UploadFileProgress);

            // 检查是否有可用的Control进行Invoke
            if (_invokeControl != null && !_invokeControl.IsDisposed)
            {
                if (_invokeControl.InvokeRequired)
                {
                    _invokeControl.Invoke(count, msg);
                }
                else
                {
                    count(msg);
                }
            }
            else
            {
                // 如果没有Control，直接调用
                count(msg);
            }
        }
        private void UploadFileProgress(string msg)
        {
            msg = "(" + m_iSerialFileCount.ToString() + "/" + m_iSerialTotalFiles.ToString() + ")" + msg;
            SendEventUpdateLoadingIndicatorNotify(msg);
        }
        #endregion
        #region 下载完成委托及事件响应
        private delegate void SerialDownloadFinish(bool finish);
        // Update the method signature to ensure nullability annotations match the EventHandler delegate
        private void SerialDownloadFinishEvent(object? sender, EventArgs e)
        {
            bool finish = (bool)sender!; // Use null-forgiving operator (!) to assert sender is not null
            SerialDownloadFinish status = new SerialDownloadFinish(SerialUploadFileResult);

            // 检查是否有可用的Control进行Invoke
            if (_invokeControl != null && !_invokeControl.IsDisposed)
            {
                if (_invokeControl.InvokeRequired)
                {
                    _invokeControl.Invoke(status, finish);
                }
                else
                {
                    status(finish);
                }
            }
            else
            {
                // 如果没有Control，直接调用
                status(finish);
            }
        }
        private void SerialUploadFileResult(bool result)
        {
            m_bSerialNextFile = result;
        }
        #endregion

        // 添加缺失的方法
        private void SendEventUpdateLoadingIndicatorNotify(string msg)
        {
            // 这里可以添加更新UI加载指示器的逻辑
            // 例如：触发事件、更新进度条等
            LogManager.Debug($"下载进度: {msg}");
        }

        // 添加设置文件计数的方法
        public void SetFileCount(int currentFile, int totalFiles)
        {
            m_iSerialFileCount = currentFile;
            m_iSerialTotalFiles = totalFiles;
        }

        public object DownloadViaSerial(object param)
        {
            try
            {
                // 检查是否已经在下载中
                if (_isDownloading)
                {
                    return ApiResponse.Fail("已有下载任务在进行中");
                }

                if(_serialPortManager == null)
                {
                    return ApiResponse.Fail("串口管理器未初始化");
                }

                if(_ymodem == null)
                {
                    return ApiResponse.Fail("Ymodem实例未初始化");
                }

                // 先串口是否打开
                if (!_serialPortManager.IsSerialPortOpen())
                {
                    return ApiResponse.Fail("串口未打开");
                }

                // 获取download目录生成的json文件列表
                var files = Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "download"), "*.json");

                // 设置文件计数
                m_iSerialTotalFiles = files.Length;
                m_iSerialFileCount = 0;

                _isDownloading = true; // 设置下载状态

                YModemBegin();
                _ymodem.YModemBegin();
                bool bComplete = true;
                string fileName = "";
                foreach (var path in files)
                {
                    // 检查是否已停止下载
                    if (!_isDownloading)
                    {
                        bComplete = false;
                        break;
                    }

                    m_iSerialFileCount++; // 增加当前文件计数
                    _ymodem.Path = path;
                    fileName = System.IO.Path.GetFileName(path);
                    if (!SendConfigJsonFileAT(fileName, 30))
                    {
                        LogManager.Warn($"SendConfigJsonFileAT failed: {fileName}");
                        bComplete = false;
                        break;
                    }
                    _ymodem.YmodemUploadFile(false);
                    if (!_ymodem.CheckCMD("OK", 30))
                    {
                        bComplete = false;
                        break;
                    }
                    //接收不到文件回复
                    if (!_ymodem.CheckCMD(GetConfigJsonFileCheckAT(fileName), 20,true))
                    {
                        bComplete = false;
                        break;
                    }
                }
                
                string tips = "配置文件下载成功";
                if (bComplete != true)
                {
                    tips = "配置文件下载失败";
                }
                tips = fileName + " " + tips;
                LogManager.Debug(tips);

                YModemEnd();
                _ymodem.YModemEnd();

                return ApiResponse.Success("通过串口下载完成: " + tips);
            }
            catch (Exception ex)
            {
                LogManager.Error($"串口下载失败: {ex.Message}");
                return ApiResponse.Fail($"串口下载失败: {ex.Message}");
            }
            finally
            {
                _isDownloading = false; // 清理下载状态
            }
        }

        private string GetConfigJsonFileCheckAT(string filename)
        {
            //string reply = "AT+RZ=" + filename + "+OK";
            string reply = filename + "+OK";
            return reply;
        }

        // Fix for CS8602: 解引用可能出现空引用。
        // Add null checks before dereferencing `_ymodem` to ensure it is not null.

        private void YModemBegin()
        {
            if (_ymodem == null)
            {
                LogManager.Error("Ymodem instance is null. Cannot begin YModem operation.");
                return;
            }

            // 发送调试关闭命令前确保在命令模式
            _ymodem.SetMode(SerialPortMode.Command);

            string cmd = "AT+DBG=0\r\n";
            string replay = "AT+DBG+OK";
            bool bRet = _ymodem.SendCMD(cmd, replay, 3);
            string err = bRet ? "调试信息关闭成功" : "调试信息关闭失败";
            LogManager.Debug(err);

            // 准备进入Ymodem模式
            _ymodem.YModemBegin();
        }
        
        void YModemEnd()
        {
            if (_ymodem == null)
            {
                LogManager.Error("Ymodem instance is null. Cannot end YModem operation.");
                return;
            }

            // 结束Ymodem模式，切换到命令模式
            _ymodem.YModemEnd();

            string cmd = "AT+DBG=1\r\n";
            string replay = "AT+DBG+OK";
            bool bRet = _ymodem.SendCMD(cmd, replay, 3);
            string err = bRet ? "调试信息打开成功" : "调试信息打开失败";
            LogManager.Debug(err);
        }

        private bool SendConfigJsonFileAT(string filename, int cycle = 3)
        {
            if (_ymodem == null)
            {
                LogManager.Error("Ymodem instance is null. Cannot send configuration JSON file.");
                return false;
            }

            string cmd = "AT+RZ=" + filename + "\r\n";
            string reply = m_strDevDWFirewareRsp;
            bool bRet = _ymodem.SendCMD(cmd, reply, cycle);
            LogManager.Debug(cmd);
            LogManager.Debug(bRet.ToString());

            return bRet;
        }

        public void SerialFirewareDownload(string fireware)
        {
            if(_ymodem == null)
            {
                LogManager.Error("Ymodem instance is null");
                return;
            }

            string tips = "第一步，重启设备成功";
            
            // 确保在命令模式下发送重启命令
            _ymodem.SetMode(SerialPortMode.Command);
            
            //第一步，重启设备
            bool bRet = SendCMD(DeviceCMDType.RESET, 5, m_strDevResetRsp, "", true, false);

            do
            {
                //第二步，设备重启成功，进入升级固件状态
                LogManager.Debug($"BtnSerialFirewareDownload{bRet}");
                if (bRet)
                {
                    LogManager.Debug(tips);
                    bRet = SendCMD(DeviceCMDType.DWFIREWARE, 20, m_strDevDWFirewareRsp, "进入升级固件状态", true, false);
                    tips = "第二步，进入升级固件状态";
                }
                else
                {
                    tips = "第一步，重启设备失败";
                    break;
                }
            } while (false);

            try
            {
                //第三步，进入升级固件状态后，开始发送固件
                if (bRet && _ymodem != null)
                {
                    LogManager.Debug(tips);
                    _ymodem.Path = fireware;
                    // 开始Ymodem文件传输
                    _ymodem.YmodemUploadFile();
                    tips = "固件发送成功";
                    if (m_bSerialNextFile != true)
                    {
                        tips = "固件发送失败";
                    }
                }
                else
                {
                    tips = "第二步，进入升级固件状态失败";
                    // 确保切换回命令模式
                    if (_ymodem != null)
                    {
                        _ymodem.SetMode(SerialPortMode.Command);
                    }
                }
            }
            catch (Exception my)
            {
                LogManager.Error(my.Message);
                // 异常时确保切换回命令模式
                _ymodem.SetMode(SerialPortMode.Command);
            }
            
            LogManager.Debug(tips);
        }

        public void SerialReset()
        {
            bool bRet = SendCMD(DeviceCMDType.RESET, 3, m_strDevResetRsp);
            LogManager.Debug(bRet.ToString());
        }

        public void SerialInfo()
        {
            bool bRet = SendCMD(DeviceCMDType.INFO);
            LogManager.Debug(bRet.ToString());
        }
   
        public void SerialState()
        {
            bool bRet = SendCMD(DeviceCMDType.STATE);
            LogManager.Debug(bRet.ToString());
        }
  
        public void SerialFormat()
        {
            bool bRet = SendCMD(DeviceCMDType.FSFORMAT);
            LogManager.Debug(bRet.ToString());
        }

        public void SerialDelete()
        {
            bool bRet = SendCMD(DeviceCMDType.DELCONFIGS);
            LogManager.Debug(bRet.ToString());
        }

        private bool SendCMD(DeviceCMDType type, int cycle = 3, string myreply = "", string tips = "", bool bTips = true, bool bYModemSend = true)
        {
            if (_serialPortManager == null)
            {
                LogManager.Error("SerialPortManager instance is null");
                return false;
            }

            if (_ymodem == null)
            {
                LogManager.Error("Ymodem instance is null");
                return false;
            }

            if ( !_serialPortManager.IsSerialPortOpen())
            {
                LogManager.Debug("SendCMD ymodem");
                return false;
            }

            if (!m_dictDeviceTypeTips.ContainsKey(type))
            {
                LogManager.Debug("SendCMD m_dictDeviceTypeTips" + type);
                return false;
            }

            if (bYModemSend)
            {
                LogManager.Debug("ymodem.YModemBegin();");
                _ymodem.YModemBegin();
            }

            string at = "AT";
            string add = "+";
            string ok = "OK";
            string newline = "\r\n";

            string cmd = type.ToString();
            cmd = at + add + cmd;
            string replyCheck = cmd + add + ok;
            cmd += newline;
            LogManager.Debug(cmd);

            string temp = myreply.Length == 0 ? replyCheck : myreply;
            bool bRet = _ymodem.SendCMD(cmd, temp, cycle);
            string err = bRet ? "操作已成功" : "操作失败";
            if (tips.Length > 0)
            {
                err = bRet ? "成功" : "失败";
                err = tips + err;
            }
            if (!bTips)
            {
                err = "请等待";
            }
            if (bYModemSend)
            {
                LogManager.Debug("ymodem.YModemEnd();");
                _ymodem.YModemEnd();
            }
            return bRet;
        }
    }
}
